package org.gienah.testing.xml;

import java.util.HashMap;
import java.util.List;
import java.util.Map;

import org.springframework.beans.factory.support.BeanDefinitionBuilder;
import org.springframework.util.xml.DomUtils;
import org.w3c.dom.Element;

/**
 * @author Santiago L. Valdarrama
 * @since E0.2
 */
public class TestBeanDefinitionParser
		extends DependentBeanDefinitionParser {

	private static final String ELEMENT_METHOD = "method";
	private static final String ATTRIBUTE_NAME = "name";
	private static final String ATTRIBUTE_TRANSACTIONAL = "transactional";
	private static final String ATTRIBUTE_IGNORE = "ignore";

	@Override
	@SuppressWarnings("unchecked")
	protected Class getBeanClass(final Element element) {
		return TestDefinition.class;
	}

	@Override
	protected void parse(final Element element, final BeanDefinitionBuilder builder) {
		super.parse(element, builder);

		boolean ignore = Boolean.valueOf(element.getAttribute(ATTRIBUTE_IGNORE));
		boolean transactional = Boolean.valueOf(element.getAttribute(ATTRIBUTE_TRANSACTIONAL));

		builder.addConstructorArg(ignore);
		builder.addConstructorArg(transactional);
		builder.addConstructorArg(parseMethods(element));
	}

	@SuppressWarnings("unchecked")
	private Map<String, MethodDefinition> parseMethods(final Element element) {
		Map<String, MethodDefinition> methods = new HashMap<String, MethodDefinition>();

		List elements = DomUtils.getChildElementsByTagName(element, ELEMENT_METHOD);
		if (elements != null && elements.size() > 0) {
			for (Object iterator : elements) {
				Element method = (Element) iterator;
				String name = method.getAttribute(ATTRIBUTE_NAME);

				if (methods.containsKey(name)) {
					throw new IllegalArgumentException("There are multiple occurrences of method '" + name + "' inside the same test class.");
				}

				boolean ignore = Boolean.valueOf(method.getAttribute(ATTRIBUTE_IGNORE));
				boolean transactional = Boolean.valueOf(method.getAttribute(ATTRIBUTE_TRANSACTIONAL));
				methods.put(name, new MethodDefinition(name, ignore, transactional));
			}
		}

		return methods;
	}

}
